package com.lmaye.utils;

import com.lmaye.common.ErrorInfo;
import com.lmaye.exceptions.CommonException;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.message.BasicNameValuePair;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * OKHttp请求工具类
 *
 * @author lmay.Zhou
 * @date 2019/4/23 17:12 星期二
 * @qq 379839355
 * @email lmay@lmaye.com
 */
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class OkHttpUtils {
    /**
     * 初始化 OkHttpClient
     */
    private final static OkHttpClient CLIENT = new OkHttpClient.Builder()
            .connectionPool(new ConnectionPool(20, 5, TimeUnit.MINUTES))
            .connectTimeout(30, TimeUnit.SECONDS)
            .readTimeout(60, TimeUnit.SECONDS)
            .writeTimeout(60, TimeUnit.SECONDS)
            .build();

    /**
     * 请求
     *
     * @param config 配置
     * @return
     */
    public static String request(OKHttpConfig config) {
        Response response = execute(config);
        byte[] bytes = new byte[0];
        try {
            bytes = response.body().bytes();
        } catch (IOException e) {
            log.error(String.format("Get response body error, Request：%s,Response：%s", config, response));
        }
        String result = new String(bytes);
        log.warn("OKHttp请求参数：" + config + "，返回结果：" + result);
        return result;
    }

    /**
     * 通用执行方法
     *
     * @param config 请求配置
     * @return 请求结果
     */
    public static Response execute(OKHttpConfig config) {
        String url = config.getUrl();
        Request.Builder builder = new Request.Builder();
        builder.url(url);

        if (config.getHeaders() != null && !config.getHeaders().isEmpty()) {
            config.getHeaders().forEach(builder::addHeader);
        }

        //有请求体优先请求体
        if (config.getRequestBody() != null) {
            builder.method(config.getMethod().getValue(), config.getRequestBody());
        } else {
            // GET请求直接拼接参数到URL
            if (Objects.equals(config.getMethod(), Method.GET)) {
                if (config.getParams() != null && !config.getParams().isEmpty()) {
                    String queryParams = config.getParams().entrySet().stream()
                            .map(entry -> String.format("%s=%s", entry.getKey(), entry.getValue()))
                            .collect(Collectors.joining("&"));
                    url = String.format("%s%s%s", url, url.contains("?") ? "&" : "?", queryParams);
                }

                builder.url(url).get();
            } else {
                //POST请求组装参数
                if (Objects.equals(config.getMediaType(), MediaType.FORM)) {
                    String data = getData(config.getParams());
                    config.setData(data);
                }
                log.info("Request Params: {}", JsonUtils.writeValueAsString(config.getData()));
                RequestBody requestBody = RequestBody.create(okhttp3.MediaType.parse(config.getMediaType().getValue()), config.getData());
                builder.method(config.getMethod().getValue(), requestBody);
            }
        }

        try {
            Response response = CLIENT.newCall(builder.build()).execute();
            return response;
        } catch (Exception e) {
            throw new CommonException(ErrorInfo.error(StringUtils.isNotBlank(e.getMessage()) ? e.getMessage() : "请求失败"), e);
        }
    }

    /**
     * 获取url编码数据
     *
     * @param params
     * @return
     */
    private static String getData(Map<String, Object> params) {
        if (params != null && !params.isEmpty()) {
            List<BasicNameValuePair> paramList = new ArrayList<>();
            for (Map.Entry<String, Object> entry : params.entrySet()) {
                String value = "";
                if (null != entry.getValue()) {
                    value = entry.getValue().toString();
                }
                paramList.add(new BasicNameValuePair(entry.getKey(), value));
            }
            return URLEncodedUtils.format(paramList, "UTF-8");
        }
        return null;
    }
}
